﻿#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <string>
#include <iomanip>
#include "ADT_L1.h" // AltaAPI头文件


// 本次使用的是
// -PMC-A429HD Board 1 SN 2012-00280
// --A429 Device/Bank 1
// --A429 Device/Bank 2
// -PMC-A429HD Board 2 SN 2012-00277
// --A429 Device/Bank 1
//  -A429 Device/Bank 2

// PMC板卡通常有多个Bank，每个Bank有16个通道
// 板卡1的通道号范围：1-16 (Bank 1), 17-32 (Bank 2)
// 板卡2的通道号范围：33-48 (Bank 1), 49-64 (Bank 2)
// 板卡3的通道号范围：65-80 (Bank 1), 81-96 (Bank 2)
// 板卡4的通道号范围：97-112 (Bank 1), 113-128 (Bank 2) 

// 板卡配置常量 (根据PMC板卡特性调整)
constexpr uint32_t BOARD_TYPE = ADT_PRODUCT_PMCA429;  // PMC-A429板卡
constexpr uint32_t BOARD_NUM = ADT_DEVID_BOARDNUM_01; // 第一块板卡
constexpr uint32_t BANK_NUM = ADT_DEVID_BANK_02;      // Bank2
constexpr uint32_t CHANNELS[] = { 83, 84, 85, 86 };   // 目标通道
constexpr size_t NUM_CHANNELS = sizeof(CHANNELS) / sizeof(CHANNELS[0]);

// 设备ID构建宏 (PMC板卡专用)
// 设备ID由背板类型、板卡类型、板卡编号、通道类型和Bank编号组成
// 
// 背板类型，4位字段，占DEVID最高4位
// ----ADT_DEVID_BACKPLANETYPE_PCI (0x1000 0000):PCI/PCIe接口(如PCI1553、PCIe4LA429板卡)
// ----ADT_DEVID_BACKPLANETYPE_ENET(0x2000 0000):以太网接口(如ENET1553、ENETA429板卡)
// ----ADT_DEVID_BACKPLANETYPE_USB (0x3000 0000):USB接口(如USB-MA4、NLINE-U1553板卡)
// ----ADT_DEVID_BACKPLANETYPE_SIMULATED (0x0000 0000):模拟接口(仅用于AltaView软件测试，不支持实际硬件)
// 例如：0x00010022表示PMC板卡1的Bank2
// 
// 板卡类型，8位字段，占DEVID的第24-31位(0x0FF0 0000),每个型号对应唯一值
// ----1553专用板卡：ADT_DEVID_BOARDTYPE_PMC1553(PMC1553板卡)、ADT_DEVID_BOARDTYPE_PCIE4L1553(PCIe4通道1553板卡)
// ----ARINC429专用板卡：ADT_DEVID_BOARDTYPE_PCIA429(PMCA429板卡)、ADT_DEVID_BOARDTYPE_ENETA429(以太网A429设备)
// ----复合功能板卡：ADT_DEVID_BOARDTYPE_PMCMA4(PMC形态，同时支持1553和A429)
//
// 板卡编号，4位字段，占DEVID的第20-23位(0x000F 0000)，从0开始计数
// ----ADT_DEVID_BOARDNUM_01 (0x0000 0000):第一块板卡
// ----ADT_DEVID_BOARDNUM_02 (0x0001 0000):第二块板卡
//
// 通道类型：8位字段，占DEVID的第12-19位(0x0000 FF00)，文档中核心类型：
// ----ADT_DEVID_CHANNELTYPE_1553 (0x0000 0100):1553通道
// ----ADT_DEVID_CHANNELTYPE_A429 (0x0000 0200):ARINC429通道
// ----ADT_DEVID_CHANNELTYPE_GLOBALS(0x0000 0100):板卡级全局逻辑设备(用于控制板卡全局寄存器、IRIG时间码、温度监测等)
//
// 通道组编号，8位字段，占DEVID的第0-7位(0x0000 00FF)，从0开始计数
// ----ADT_DEVID_BANK_01 (0x0000 0001):Bank1
// ----ADT_DEVID_BANK_02 (0x0000 0002):Bank2
#define BUILD_PMC_DEVID (BOARD_TYPE | BOARD_NUM | ADT_DEVID_CHANNELTYPE_A429 | BANK_NUM)


// #pragma pack(push, 1) 用于设置结构体的内存对齐方式为1字节对齐，确保结构体成员按顺序紧密排列，没有填充字节
// #pragma pack(pop) 用于恢复之前的对齐设置
// 这对于硬件接口和网络协议等场景非常重要，确保数据结构与预期的二进制格式一致
// #pragma pack(push, 1)通常与#pragma pack(pop)成对使用，以限定对齐设置的作用范围

// ARINC429数据字解析结构体
#pragma pack(push, 1)
typedef struct {
    uint32_t label : 8;   // 8位  0-7:标签字段(八进制)
    uint32_t sdi : 2;     // 2位  8-9:源/目标标识符
    uint32_t data : 19;   // 19位 10-28:数据字段(BNR/BCD编码)
    uint32_t ssm : 2;     // 2位  29-30:符号状态矩阵
    uint32_t parity : 1;  // 1位  31:奇偶校验位
} ARINC429_Word;
#pragma pack(pop)

/**
 * @brief 初始化A429板卡和接收通道
 * @param devID 设备标识符
 * @return 成功返回ADT_SUCCESS，否则返回错误码
 */
ADT_L0_UINT32 InitializeBoard(uint32_t devID) {
	ADT_L0_UINT32 status = ADT_SUCCESS; // 声明状态变量，并初始化为成功状态

    // 1. 初始化设备 (PMC板卡专用参数)
    uint32_t numIQEntries = 1024; // 中断队列大小

	// 初始化设备，告知驱动程序准备管理指定硬件devID
	status = ADT_L1_A429_InitDevice(devID, numIQEntries); 
    if (status != ADT_SUCCESS) {
        std::cerr << "PMC板卡初始化失败: " << ADT_L1_Error_to_String(status) << std::endl;
        return status;
    }
    std::cout << "PMC板卡初始化成功" << std::endl;

    // 2. 初始化接收通道
    for (size_t i = 0; i < NUM_CHANNELS; ++i) {
		// 计算Bank内通道索引 (83%16-1=2，84%16-1=3，85%16-1=4，86%16-1=5)
        uint32_t bank_index = CHANNELS[i] % 16 - 1;

        // PMC板卡通道参数配置
        uint32_t bitRate = 100000;    // 100kbps (ARINC429高速率，必须与飞仿模型速率匹配)
		uint32_t numRxP = 1024;       // 每个通道的FIFO接收缓冲区大小，用于存储接收到的数据包
        uint32_t options = 0;         // 默认选项

		// 初始化接收通道。配置通道的比特率、缓冲区大小和选项
        status = ADT_L1_A429_RX_Channel_Init(devID, bank_index, bitRate, numRxP, options);
        if (status != ADT_SUCCESS) {
            std::cerr << "通道" << CHANNELS[i]
                << "初始化失败: " << ADT_L1_Error_to_String(status) << std::endl;
            return status;
        }
        std::cout << "通道" << CHANNELS[i] << "初始化成功" << std::endl;
    }

    return ADT_SUCCESS;
}

/**
 * @brief 解析ARINC429数据字
 * @param dataWord 原始32位数据字
 * @param parsedWord 解析后的数据结构
 */
void ParseARINC429Word(uint32_t dataWord, ARINC429_Word& parsedWord) {
    // 使用内存拷贝确保位字段正确解析
    // 将原始数据字映射到结构体成员​​。
    // 其正确性严重依赖于​​硬件数据的内存布局​​与​​结构体位域定义​​的精确匹配。
    memcpy(&parsedWord, &dataWord, sizeof(uint32_t));

    // 调整字节序 (如果需要)
    // parsedWord.label = __builtin_bswap32(parsedWord.label) & 0xFF;
}

/**
 * @brief 获取SSM状态描述
 * @param ssm SSM值
 * @return 状态描述字符串
 */
std::string GetSSMDescription(uint32_t ssm) {
    switch (ssm) {
    case 0: return "故障警告";
    case 1: return "无计算数据";
    case 2: return "功能测试";
    case 3: return "正常操作";
    default: return "未知状态";
    }
}

/**
 * @brief 主处理函数
 */
int main() {
    ADT_L0_UINT32 status = ADT_SUCCESS;
    uint32_t devID = BUILD_PMC_DEVID; // PMC板卡设备ID

    // 1. 初始化板卡和通道
    status = InitializeBoard(devID);
    if (status != ADT_SUCCESS) {
        std::cerr << "板卡初始化失败，程序终止" << std::endl;
        return 1;
    }

    // 2. 创建多通道接收缓冲区
    // BUFFER_SIZE定义了每次调用ADT_L1_A429_RXMC_ReadNewRxPs函数时，从驱动程序的多通道接收缓冲区中能读取的最大数据包数量​​。
	constexpr uint32_t BUFFER_SIZE = 100; 
    

    // 创建一个软件端的缓冲区，用于​​批量接收​​来自多个通道的数据。这是高效处理多通道数据的关键。
    status = ADT_L1_A429_RXMC_BufferCreate(devID, BUFFER_SIZE);
    if (status != ADT_SUCCESS) {
        std::cerr << "缓冲区创建失败: " << ADT_L1_Error_to_String(status) << std::endl;
        goto cleanup;
    }

    // 3. 启动接收通道
    for (size_t i = 0; i < NUM_CHANNELS; ++i) {
        uint32_t bank_index = CHANNELS[i] % 16 - 1;
        // 循环启动所有目标通道（83, 84, 85, 86）。
        // 此后，硬件开始监听总线并将数据存入各自的FIFO。
        ADT_L1_A429_RX_Channel_Start(devID, bank_index);
    }
    std::cout << "开始接收数据..." << std::endl;

    // 4. 主数据读取循环
    while (true) {
        // 声明一个数组，用于存放从API读取到的数据包。
        ADT_L1_A429_RXP rxPackets[BUFFER_SIZE];
        uint32_t numReceived = 0;

        // ADT_L1_A429_RXMC_ReadNewRxPs从之前创建的多通道缓冲区中​​批量读取​​数据包。
        // &numReceived: ​​输出参数​​。函数返回后，此变量会包含​​实际读取到的数据包数量​​。
        // rxPackets: ​​输出参数​​。读取到的数据包将被填充到这个数组中。
        status = ADT_L1_A429_RXMC_ReadNewRxPs(devID, BUFFER_SIZE, &numReceived, rxPackets);
        if (status != ADT_SUCCESS) {
            std::cerr << "数据读取错误: " << ADT_L1_Error_to_String(status) << std::endl;
            continue;
        }

        // 解析每个数据包
        for (uint32_t i = 0; i < numReceived; ++i) {
            const ADT_L1_A429_RXP& pkt = rxPackets[i];
            ARINC429_Word parsedWord;

            // 提取通道号 (Bank索引转全局通道号)
			// 全局通道号 = (Bank编号 * 16) + (Bank内通道索引 + 1)
			// pkt.Control的低8位存储了Bank内的通道索引 (0-15)
			// pkt.Control & 0xFF 提取低8位
            // global_ch将Bank内索引转换为​​全局通道号
            uint32_t global_ch = BANK_NUM * 16 + (pkt.Control & 0xFF) + 1;

            // 只处理目标通道数据
            bool is_target = false;
            for (auto ch : CHANNELS) {
                if (ch == global_ch) {
                    is_target = true;
                    break;
                }
            }
            if (!is_target) continue;

            // 解析ARINC429数据字
            ParseARINC429Word(pkt.Data, parsedWord);

            // 输出解析结果
            std::cout << "\n=== 通道 " << global_ch << " 数据 ===" << std::endl;

			// std::setw和std::setfill用于格式化输出，需要#include <iomanip>
			// std::setw(3)设置宽度为3，std::setfill('0')用'0'填充空位
            // std::hex用于十六进制输出，std::dec用于十进制输出
            std::cout << "标签(八进制): " << std::oct << std::setw(3) << std::setfill('0')
                << parsedWord.label << std::dec << std::endl;
            std::cout << "SDI: " << parsedWord.sdi << std::endl;
            std::cout << "数据字段: 0x" << std::hex << std::setw(5) << std::setfill('0')
                << parsedWord.data << std::dec << std::endl;
            std::cout << "SSM状态: " << GetSSMDescription(parsedWord.ssm)
                << " (" << parsedWord.ssm << ")" << std::endl;
            std::cout << "奇偶校验: " << (parsedWord.parity ? "奇校验" : "偶校验") << std::endl;
			// 输出时间戳 (纳秒),用于分析数据的先后顺序和间隔
            std::cout << "时间戳: " << pkt.timeLow << " ns" << std::endl;

            // 这里可以添加具体的数据解码逻辑 (BNR/BCD转换)
            // 根据标签字段确定数据格式和转换方式
        }

        // 每次循环后让当前线程阻塞100毫秒。这是为了​​降低CPU占用率​​，避免循环空转。
		std::this_thread::sleep_for(std::chrono::milliseconds(100));

cleanup:
    // 5. 资源清理
    // 先​​停止​​通道接收，然后​​关闭​​通道，再​​释放​​缓冲区，最后​​关闭​​设备。
    std::cout << "正在清理资源..." << std::endl;

    // 停止接收通道
    for (size_t i = 0; i < NUM_CHANNELS; ++i) {
        uint32_t bank_index = CHANNELS[i] % 16 - 1;
        ADT_L1_A429_RX_Channel_Stop(devID, bank_index);
        ADT_L1_A429_RX_Channel_Close(devID, bank_index);
    }

    // 释放缓冲区
    ADT_L1_A429_RXMC_BufferFree(devID, bufferHandle);

    // 关闭设备
    ADT_L1_CloseDevice(devID);

    std::cout << "程序正常退出" << std::endl;
    return 0;
}